package com.fisherman.config;

import com.fisherman.aop.LogAspects;
import com.fisherman.aop.MathCalculator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * @User: fisherman
 * @Date: 2020/5/23 11:07
 *
 *
 * AOP：【动态代理】
 *      指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式
 *
 *      1，导入aop模块  spring aop  [spring-aspects]
 *      2.定义一个业务逻辑类  【MathCalculator】，在业务逻辑运行的时候可以吧日志打印一下（方法之前，方法运行结束，方法出现异常）
 *      3.定义一个日志切面（LogAspects），切面类面的方法需要动态感知MathCalculator.div运行到哪里然后执行
 *              通知方法：
 *                      前置通知(@Before);  logStart，在目标方法(div)运行之前
 *                      后置通知(@After)； logEnd    在目标方法（div）运行之后
 *                      返回通知(@AfterReturning);  logReturn    在目标方法（div）正常返回之后运行
 *                      异常通知(@AfterThrowing)；  logException  在目标方法（div）出现异常时运行
 *                      环绕通知(@Around)：动态代理，手动推进目标方法运行（joinPoint.procced()）
 *      4.给切面类的目标方法标注何时何地运行（通知注解）
 *      5.将切面类和业务逻辑类（目标方法所在的类）都加入到容器中;
 *      6.必须告诉spring那个类是切面类（给切面类加上一个注解@Aspect）
 *      7.给配置类加@EnableAspectJAutoProxy【开启基于注解的aop模式】
 *          在spring中的很多@Enablexxx；
 *aop三步曲
 *      1.将业务逻辑组件和切面类都加入到容器中，告诉spring那个是切面类（@Aspect）
 *      2.切面类上的每一个通知方法上标注通知注解，告诉spring何时何地运行（切入点表达式）
 *      3.开启基于注解的aop模式：@EnableAspectJAutoProxy
 *
 *
 *aop原理:【看给容器注入的什么组件，这个组件什么时间工作，这个是组件的功能是什么】
 *      @EnableAspectJAutoProxy;
 *
 * 1.@EnableAspectJAutoProxy是什么
 *      @Import(AspectJAutoProxyRegistrar.class)，给容器中导入AspectJAutoProxyRegistrar
 *      利用AspectJAutoProxyRegistrar自定义给容器中注入bean
 *      internalAutoProxyCreator = AnnotationAwareAspectJAutoProxyCreator
 *
 *      给容器中注册一个AnnotationAwareAspectJAutoProxyCreator
 *2.给容器中注册一个AnnotationAwareAspectJAutoProxyCreator是什么
 *      AnnotationAwareAspectJAutoProxyCreator
 *          -》AspectJAwareAdvisorAutoProxyCreator
 *              ->AbstractAdvisorAutoProxyCreator
 *                  ->AbstractAutoProxyCreator
 *                      implements SmartInstantiationAwareBeanPostProcessor,BeanFactoryAware
 *                      关注后置处理器（在bean初始化完成前后做的事情），自动装配BeanFactory
 *
 *AbstractAutoProxyCreator.setBeanFactory()
 * AbstractAutoProxyCreator.后置处理器的逻辑
 *
 * AbstractAdvisorAutoProxyCreator.seBeanFactory()->initBeanFactory()
 *
 * AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
 *
 * 流程：
 *      1.传入配置类，创建ioc容器
 *      2.注册配置类，调用refresh()刷新容器
 *      3.registerBeanPostProcessors(beanFactory,注册bean的后置处理器，来方便拦截bean的创建)
 *          1.先获取ioc容器中已经定义的需要创建对象所有BeanPostProcessor
 *          2.给容器加别的BeanPostProcessor
 *          3.优先注册了PriorityOrdered接口BeanPostProcessor
 *          4.在容器中注册实现了Order接口的BeanPostProcessor
 *          5.注册没实现优先级接口的BeanPostProcessor
 *          6.注册BeanPostProcessor，实际上就是创建BeanPostProcessor对象，保存在容器中
 *              创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
 *              1.创建bean的实例
 *              2.populateBean,给bean的各种属性赋值
 *              3.initializeBean:初始化bean
 *                  1.invokeAwareMethods(),处理Aware接口的方法回调
 *                  2.applyBeanPostProcessorBeforeInitialization()应用后置处理器的postProcessBeforeInitialization()
 *                  3.invokeInitMethods()执行自定义的初始化反方法
 *                  4.applyBeanPostProcessorBeforeInitialization(),执行后置处理器的postProcessAfterInitialization()
 *              4.BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功，--》aspectJAdvisorsBuilder
 *          7.把BeanPostProcessor注册到的BeanFactory中
 *              beanFactory.addBeanPostProcessor(postProcessor)
 *======以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程======
 *          AnnotationAwareAspectJAutoProxyCreator =》InstantiationAwareBeanPostProcessor()
 *
 *       4.finishBeanFactoryInitialization(beanFactory),完成BeanFactory初始化工作，创建剩下的单实例bean
 *            1.遍历获取容器中所有bean，依次创建对象getBean（beanName）
 *                  getBean -》 doGetBean()  -> getSingleton() ->
 *            2.创建bean
 *                  【AnnotationAwareAspectJAutoProxyCreator在所有bean创建完成之前会有一个拦截，InstantiationAwareBeanPostProcessor，会调用postProcessBeforeInstantiation（）】
 *                  1.先从缓存中获取bean，如果获取到，说明bean之前被创建过，直接使用，否则再创建
 *                     只要创建好的bean就会被缓存起来
 *                  2.createBean(); 创建bean  AnnotationAwareAspectJAutoProxyCreator会在任何bean创建之前先尝试返回bean实例
 *                      [BeanPostProcessor是在Bean对象创建完成初始化前后调用的]
 *                      [InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试使用后置处理器返回对象]
 *                         1.resolveBeforeInstantiation（beanName，mbdToUse）；解析BeforeInstantiation
 *                              希望后置处理器在此能返回一个代理对象，如果能返回代理对象就使用，不能就继续
 *                              1.后置处理器先尝试返回对象
 *                                  bean = applyBeanPostProcessorsBeforeInstantiation;
 *                                  //拿到所有的后置处理器，如果是InstantiationAwareBeanPostProcessor，
 *                                  //就执行postProcessBeforeInstantiation
 *                                  if(bean != null){
 *                                      bean = applyBeanPostProcessorsAfterInitialization(bean,beanName)
 *                                  }
 *
 *                         2.doCreateBean（beanName，mbdToUse，args）；真正的去创建一个bean实例和3.6一样
 *
 * AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】的作用
 * 1.每一个bean创建之前，调用postProcessBeforeInstantiation();
 *      关心MathCalculator和LogAspect的创建
 *      1.判断当前bean是否在advisedBeans中，（保存了所有需要增强的bean）
 *      2.判断当前bean是否是基础类型的Advice，Pointcut.class.isAssignableFrom(beanClass) ||
 *          Advisor.class.isAssignableFrom(beanClass) ||
 *          AopInfrastructureBean.class.isAssignableFrom(beanClass);
 *          或者是否是切面，(@Aspect)
 *      3.是否需要跳过
 *            1.获取增强器，（切面里面的通知方法）【List<Advisor> candidateAdvisors】
 *                  每一个封装的通知方法增强器是InstantiationModelAwarePointcutAdvisor;
 *                  判断每一个增强器是否是AspectJPointcutAdvisor类型的：返回true
 *            2。永远返回false
 *2.创建对象
 *      PostProcessorsAfterInitialization
 *          return wrapIfNecessary(bean,beanName,cacheKey);//包装需要的情况下
 *           1.获取当前bean的所有增强器（通知方法）
 *               1.找到候选所有的增强器（找打那些通知方法需要切入当前bean方法的）
 *               2.获取到能在bean使用的增强器0
 *               3.给增强器排序
 *           2.保存当前bean在advisedBeans中，
 *           3.如果当前bean需要增强，只需要创建一个bean的代理对象
 *                  1.获取所有增强器（通知方法）
 *                  2.保存到proxyFactory
 *                  3.创建代理对象，Spring自动决定
 *                          jdkDynamicAopProxy(config);jdk动态代理
 *                          ObjenesisCglibAopProxy(config)；cglib代理
 *                   4.给容器中返回当前组件使用cglib增强了的代理对象
 *                   5.以后容器获取到的就是这个组件代理对象，执行目标方法的时候，代理对象就会执行通知方法的流程
 *3.目标方法的执行
 *      容器中保存组件的代理对象（cglib增强后的对象），这个对象里面保存了详细信息（比如增强器，目标对象，xxx）
 *           1.CglibAopProxy.intercept();拦截目标方法的执行
 *           2.根据ProxyFactory对象获取要执行的目标方法拦截器链
 *                  List<Object>chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);
 *                  1.List<Object>interceptorList保存所有拦截器
 *                      一个默认的ExposeInvocationInterceptor 和 4个增强器
 *                  2.遍历所有的增强器，将其转化为Interceptor
 *                       registry.getInterceptors(advisor)
 *                  3.将增强器转为List<MethodInterceptor>;
 *                         如果是MethodInterceptor,直接加入集合中
 *                         如果不是，使用AdvisorAdapter将增强器转为Interceptor
 *                         转换完成返回MethodInterceptor数组
 *           3.如果没有拦截器链，直接执行目标方法
 *                  连接器链（每个通知方法又被包装为方法拦截器，利用MethodInterceptor机制）
 *           4.如果有拦截器链，把需要执行的目标对象，目标方法，拦截器链等信息创建一个CglibMethodInvocation对象
 *              并调用Object retVal =  mi.proceed()
 *           5.拦截器的触发过程；
 *              1.如果没有拦截器执行目标方法，或者拦截的索引和拦截器数组-1大小一样（指定到最后一个拦截器执行目标方法）
 *              2.链式获取每一个连接器，拦截器执行invoke方法，每一个拦截器等待下一个拦截器执行完成后返回以后再执行
 *                  拦截器链的机制，保证通知方法与目标方法的执行排序
 *
 * 总结：
 *          1.@EnableAspectJAutoProxy;  开启aop功能
 *          2.@EnableAspectJAutoProxy  会给容器中注册一个组件AnnotationAwareAspectJAutoProxyCreator
 *          3.AnnotationAwareAspectJAutoProxyCreator是一个后置处理器
 *          4.容器的创建流程
 *                  1.registerBeanPostProcessors()注册后置处理器
 *                  2.finishBeanFactoryInitialization()初始化剩下的单实例bean
 *                          1.创建业务组件和切面组件
 *                          2.AnnotationAwareAspectJAutoProxyCreator拦截器组件的创建过程
 *                          3.组件创建完之后，判断组件是否需要增强
 *                              是：切面的通知方法，包装成增强器（Advisor）；给业务逻辑组件创建一个代理对象（cglib）
 *           5.执行目标方法
 *                  1,代理对象执行目标方法
 *                  2，CglibAopProxy.intercept()
 *                      1.得到目标方法的拦截器（增强器包装成拦截器MethodInterceptor）
 *                      2.利用拦截器的链式机制，一次进入每一个拦截器进行执行
 *                      3.效果：
 *                          正常执行：前置通知-》目标方法-》后置通知-》返回通知
 *                          出现异常：前置通知-》目标方法-》后置通知-》异常通知
 *
 */
@EnableAspectJAutoProxy
@Configuration  //告诉spring这个是注册类
public class MainConfigOfAop {

    //业务逻辑类加入的容器中
    @Bean
    public MathCalculator calculator(){
        return new MathCalculator();
    }

    //切面类加入到容器中
    @Bean
    public LogAspects logAspects(){
        return new LogAspects();
    }
}

